﻿// <copyright file="Ascii.cs" company="TenneySoftware">
// Copyright (c) 2009 All Right Reserved
// </copyright>
// <author>Robert Tenney</author>
// <email>rob10e@yahoo.com</email>
// <date>April 2009</date>
// <summary>ASCII style graphics manager based off of SimpleTileGDI+ libraries.</summary>// License:

// Product: ASCII_GDI.csproj
// Author: Robert N. Tenney
// Copyright: 9/23/2009

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation version 2
// of the License.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// This license covers all source code, binaries, and other materials related to this project
// unless specifically stated otherwise.

using System;
using System.Drawing;
using System.IO;

using TenneySoftware.Engine;
using TenneySoftware.Engine.Debug;
using TenneySoftware.Engine.GraphicsManager;

namespace TenneySoftware.GraphicsManagers {
    /// <summary>
    /// A graphics manager that draws the traditional ASCII characters for tiles which inherits the SimpleTile GDI+ class.
    /// </summary>
    public class Ascii : SimpleTileGdi, IGraphicsManager, IDebugConsole, IDisposable {
        /// <summary>
        /// The bitmaps used to generate the tiles. Each tile has a:
        /// Standard Level image,
        /// Standard Map Image,
        /// Shaded Level Image,
        /// Shaded Map Image,
        /// Highlighted Level Image,
        /// Highlight Map Image.
        /// </summary>
        private Bitmap shadeLevel, shadeMap, hiLiteLevel, hiLiteMap, levelImageLoc, mapImageLoc;

        /// <summary>
        /// The graphics object for each of the tile's images.
        /// </summary>
        private Graphics shadeLevelGr, shadeMapGr, hiLiteLevelGr, hiLiteMapGr, levelGr, mapGr;

        /// <summary>
        /// Brushes used for the tiles.
        /// </summary>
        private Brush shade, hiLite, defaultBack, background, forground, mapColor;

        /// <summary>
        /// Font used to draw the ASCII character.
        /// </summary>
        private string font = "Consolas";
        
        /// <summary>
        /// Initializes a new instance of the Ascii class.
        /// </summary>
        public Ascii() : base() {
            this.ModuleName = "Simple Tile GDI+ - ASCII";
            this.ModuleDescription = "Graphics Manager that uses traditional ASCII symbols. Uses GDI+ to draw tiles.";
            this.ModuleDisplaySurface = "GDI+";
        }

        /// <summary>
        /// Gets or sets the font to use for the ASCII characters. Must be a TrueType Font. Default is: Consolas.
        /// </summary>
        /// <value>
        /// The font to use for the ASCII characters. Must be a TrueType Font. Default is: Consolas.
        /// </value>
        public string Font {
            get {
                return this.font;
            }

            set {
                this.font = value;
            }
        }

        /// <summary>
        /// Processes the command string sent by the DebugConsole.
        /// </summary>
        /// <param name="command">Command string to process.</param>
        /// <returns></returns>
        public bool ConsoleCommand(string command) {
            return this.Processor.ConsoleCommand(command);
        }

        /// <summary>
        /// Called to process the information file associated with this module.
        /// </summary>
        public override void GenerateTiles() {
            bool staticObjects = true;
            int segCount = 3;
            string filename = Path.Combine(
                                  "GraphicsManagers",
                                  System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.Name.ToString() + "TileSet");
            string[] lines;
            string obj = "wall_stone";
            char character = 'W';
            Color findBackGround, findForeGround;
            Font asciiFont;
            string ascii;
            bool complete = false;
            int fontSizeStep = Math.Max(this.TileSizeLevel.Width, this.TileSizeLevel.Height);
            Size fontSize;
            this.levelImageLoc = new Bitmap(this.TileSizeLevel.Width, this.TileSizeLevel.Height);
            this.levelGr = Graphics.FromImage(this.levelImageLoc);
            Rectangle levelBounds = new Rectangle(Point.Empty, this.TileSizeLevel);
            levelBounds.Inflate(5, 5);
            Rectangle mapBounds = new Rectangle(Point.Empty, this.TileSizeMap);
            Rectangle characterBounds;
            this.defaultBack = new SolidBrush(Color.Magenta);
            do {
                asciiFont = new Font(this.font, fontSizeStep, GraphicsUnit.Pixel);
                fontSize = this.levelGr.MeasureString(character.ToString(), asciiFont).ToSize();
                characterBounds = new Rectangle(Point.Empty, fontSize);
                if (levelBounds.Contains(characterBounds)) {
                    complete = true;
                }

                fontSizeStep--;
            } while (!complete);

            try {
                lines = System.IO.File.ReadAllLines(filename + ".txt");
            } catch (IOException) {
                throw new IOException(filename + ".txt" + " was not found.");
            }

            foreach (string line in lines) {
                if (String.IsNullOrEmpty(line)) {
                    continue;
                }

                if (line.ToUpperInvariant() == "STATIC OBJECTS:") {
                    staticObjects = true;
                    continue;
                }

                if (line.ToUpperInvariant() == "DYNAMIC OBJECTS:") {
                    staticObjects = false;
                    continue;
                }

                try {
                    string[] segments = line.Split(new char[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    try {
                        if (string.Compare(segments[0], "player", StringComparison.OrdinalIgnoreCase) == 0) {
                            obj = segments[0];
                        } else {
                            string[] seg2 = segments[0].Split(new char[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
                            string first = string.Empty, second = string.Empty;
                            foreach (string type in Enum.GetNames(typeof(TileType))) {
                                if (string.Compare(type, seg2[0], StringComparison.OrdinalIgnoreCase) == 0) {
                                    first = seg2[0];
                                    break;
                                }
                            }
                            
                            if (seg2.GetLength(0) > 1) {
                                if (string.Compare(first, "door", StringComparison.OrdinalIgnoreCase) == 0) {
                                    foreach (string type in Enum.GetNames(typeof(DoorVariation))) {
                                        if (string.Compare(type, seg2[1], StringComparison.OrdinalIgnoreCase) == 0) {
                                            second = seg2[1];
                                            break;
                                        }
                                    }
                                } else {
                                    foreach (string type in Enum.GetNames(typeof(TileTypeVariation))) {
                                        if (string.Compare(type, seg2[1], StringComparison.OrdinalIgnoreCase) == 0) {
                                            second = seg2[1];
                                            break;
                                        }
                                    }
                                }
                            }
                            
                            if (!string.IsNullOrEmpty(first) && !string.IsNullOrEmpty(second)) {
                                obj = segments[0];
                            } else {
                                continue;
                            }
                        }
                    } catch (ArgumentException) {
                        continue;
                    }

                    character = char.Parse(segments[1]);
                    if (staticObjects) {
                        segCount = 3;
                        try {
                            findBackGround = Color.FromArgb(int.Parse(segments[2], System.Globalization.CultureInfo.CurrentCulture));
                        } catch (FormatException) {
                            try {
                                findBackGround = Color.FromArgb(Convert.ToInt32(segments[2], 16));
                            } catch (FormatException) {
                                findBackGround = Color.FromName(segments[2]);
                            }
                        }
                    } else {
                        findBackGround = Color.FromArgb(0, 0, 0, 0);
                        segCount = 2;
                    }

                    try {
                        findForeGround = Color.FromArgb(int.Parse(segments[segCount], System.Globalization.CultureInfo.CurrentCulture));
                    } catch (FormatException) {
                        try {
                            findForeGround = Color.FromArgb(Convert.ToInt32(segments[segCount], 16));
                        } catch (FormatException) {
                            findForeGround = Color.FromName(segments[segCount]);
                        }
                    }
                } catch {
                    throw new IOException("Information file was not properly formatted.");
                }

                this.background = new SolidBrush(findBackGround);
                this.forground = new SolidBrush(findForeGround);

                this.mapColor = this.background;
                switch (obj.ToUpperInvariant()) {
                    case "FLOOR_STONE":
                        this.defaultBack = this.background;
                        break;
                    case "PLAYER":
                        this.mapColor = Brushes.Blue;
                        break;
                    case "DOOR_OPEN":
                        this.mapColor = this.background;
                        break;
                    case "DOOR_CLOSED":
                        this.mapColor = this.forground;
                        break;
                }

                TileImages temp = new TileImages();
                if (staticObjects) {
                    temp.IsStatic = true;
                } else {
                    temp.IsStatic = false;
                }

                this.levelImageLoc = new Bitmap(this.TileSizeLevel.Width, this.TileSizeLevel.Height);
                this.levelGr = Graphics.FromImage(this.levelImageLoc);
                this.levelGr.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                this.mapImageLoc = new Bitmap(this.TileSizeMap.Width, this.TileSizeMap.Height);
                this.mapGr = Graphics.FromImage(this.mapImageLoc);
                ascii = character.ToString();
                fontSize = this.levelGr.MeasureString(ascii, asciiFont).ToSize();
                PointF offsetAscii = new PointF(((float)this.TileSizeLevel.Width / 2f) - ((float)fontSize.Width / 2f), ((float)this.TileSizeLevel.Height / 2f) - ((float)fontSize.Height / 2f));
                this.levelGr.FillRectangle(this.background, levelBounds);
                this.levelGr.DrawString(ascii, asciiFont, this.forground, offsetAscii);
                this.mapGr.FillRectangle(this.mapColor, mapBounds);
                temp.LevelImage = this.levelImageLoc;
                temp.MapImage = this.mapImageLoc;
                temp.Tile = obj;

                this.shadeLevel = (Bitmap)this.levelImageLoc.Clone();
                this.shadeLevelGr = Graphics.FromImage(this.shadeLevel);
                this.shade = new SolidBrush(Color.FromArgb(128, Color.Black));
                this.shadeLevelGr.FillRectangle(this.shade, new Rectangle(Point.Empty, this.TileSizeLevel));
                temp.LevelImageShadow = this.shadeLevel;

                this.shadeMap = (Bitmap)this.mapImageLoc.Clone();
                this.shadeMapGr = Graphics.FromImage(this.shadeMap);
                this.shadeMapGr.FillRectangle(this.shade, new Rectangle(Point.Empty, this.TileSizeMap));
                temp.MapImageShadow = this.shadeMap;

                this.hiLiteLevel = (Bitmap)this.levelImageLoc.Clone();
                this.hiLiteLevelGr = Graphics.FromImage(this.hiLiteLevel);
                this.hiLite = new SolidBrush(Color.FromArgb(75, Color.White));
                this.hiLiteLevelGr.FillRectangle(this.hiLite, new Rectangle(Point.Empty, this.TileSizeLevel));
                temp.LevelImageHighlight = this.hiLiteLevel;

                this.hiLiteMap = (Bitmap)this.mapImageLoc.Clone();
                this.hiLiteMapGr = Graphics.FromImage(this.hiLiteMap);
                this.hiLiteMapGr.FillRectangle(this.hiLite, new Rectangle(Point.Empty, this.TileSizeMap));
                temp.MapImageHighlight = this.hiLiteMap;

                if (string.Compare(obj, "PLAYER", StringComparison.OrdinalIgnoreCase) == 0) {
                    this.Player = temp;
                }

                this.DrawTiles.Add(temp);
            }

            TileImages temp2 = new TileImages();
            temp2.Tile = "UNSEEN";
            this.levelImageLoc = new Bitmap(this.TileSizeLevel.Width, this.TileSizeLevel.Height);
            this.levelGr = Graphics.FromImage(this.levelImageLoc);
            this.background = Brushes.Black;
            this.levelGr.FillRectangle(this.background, levelBounds);
            this.mapImageLoc = new Bitmap(this.TileSizeMap.Width, this.TileSizeMap.Height);
            this.mapGr = Graphics.FromImage(this.mapImageLoc);
            this.mapColor = Brushes.Black;
            this.mapGr.FillRectangle(this.mapColor, mapBounds);
            temp2.LevelImage = this.levelImageLoc;
            temp2.MapImage = this.mapImageLoc;
            temp2.LevelImageHighlight = this.levelImageLoc;
            temp2.MapImageHighlight = this.mapImageLoc;
            temp2.LevelImageShadow = this.levelImageLoc;
            temp2.MapImageShadow = this.mapImageLoc;
            this.DrawTiles.Add(temp2);

            this.DrawTiles.Sort();
            this.DrawTiles.Save(filename + ".bin");

            Bitmap bitmap = new Bitmap(this.TileSizeLevel.Width * this.DrawTiles.Count, (this.TileSizeLevel.Height * 3) + (this.TileSizeMap.Height * 3));
            Graphics gr = Graphics.FromImage(bitmap);
            Rectangle area = new Rectangle(Point.Empty, this.TileSizeLevel);
            Point offset = Point.Empty;
            foreach (TileImages objIm in this.DrawTiles) {
                gr.DrawImage(objIm.LevelImage, offset);
                offset.Offset(this.TileSizeLevel.Width, 0);
            }

            offset = new Point(0, this.TileSizeLevel.Height);
            foreach (TileImages objIm in this.DrawTiles) {
                gr.DrawImage(objIm.MapImage, offset);
                offset.Offset(this.TileSizeMap.Width, 0);
            }

            offset = new Point(0, this.TileSizeLevel.Height + this.TileSizeMap.Height);
            foreach (TileImages objIm in this.DrawTiles) {
                gr.DrawImage(objIm.LevelImageShadow, offset);
                offset.Offset(this.TileSizeLevel.Width, 0);
            }

            offset = new Point(0, (this.TileSizeLevel.Height * 2) + this.TileSizeMap.Height);
            foreach (TileImages objIm in this.DrawTiles) {
                gr.DrawImage(objIm.MapImageShadow, offset);
                offset.Offset(this.TileSizeMap.Width, 0);
            }

            offset = new Point(0, (this.TileSizeLevel.Height * 2) + (this.TileSizeMap.Height * 2));
            foreach (TileImages objIm in this.DrawTiles) {
                gr.DrawImage(objIm.LevelImageHighlight, offset);
                offset.Offset(this.TileSizeLevel.Width, 0);
            }

            offset = new Point(0, (this.TileSizeLevel.Height * 3) + (this.TileSizeMap.Height * 2));
            foreach (TileImages objIm in this.DrawTiles) {
                gr.DrawImage(objIm.MapImageHighlight, offset);
                offset.Offset(this.TileSizeMap.Width, 0);
            }

            bitmap.Save(filename + ".png", System.Drawing.Imaging.ImageFormat.Png);
            bitmap.Dispose();
            gr.Dispose();

            asciiFont.Dispose();
        }

        /// <summary>
        /// Disposes the resources allocated on this manager.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Dispose(true) is handled by variable.")]
        public sealed override void Dispose() {
            bool all = false;
            if (!this.AreImagesLoaded) {
                all = true;
            }

            this.Dispose(all);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the resources allocated on this manager.
        /// </summary>
        /// <param name="disposeAll">True to dispose all.</param>
        protected virtual void Dispose(bool disposeAll) {
            if (disposeAll) {
                this.mapImageLoc.Dispose();
                this.levelImageLoc.Dispose();
                this.mapGr.Dispose();
                this.levelGr.Dispose();
                this.mapColor.Dispose();
                this.defaultBack.Dispose();
                this.hiLiteLevel.Dispose();
                this.hiLiteLevelGr.Dispose();
                this.hiLiteMap.Dispose();
                this.hiLiteMapGr.Dispose();
                this.shadeLevel.Dispose();
                this.shadeLevelGr.Dispose();
                this.background.Dispose();
                this.forground.Dispose();
                this.shade.Dispose();
                this.hiLite.Dispose();
            }
        }
    }
}
